package com.crazyauntzhang.easyshopbackend.util;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 用于生成、解析、校验token
 * <p>
 * 一种密钥生成方法：
 * <pre>Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);</pre>
 * <pre>String secretString = Encoders.BASE64.encode(key.getEncoded());</pre>
 * 静态成员方式定义密钥:
 * <pre>private static final String SECRET_KEY = "9bWv9WnIAqlsDvZFAtFKQKPpKvFy7gA5JqgBzQ2mKIk=";</pre>
 * </p>
 */

@Data
@Component
public class Token {

	//签名密钥，在配置文件中是一个Base64串
	private static String secretString;
	//token有效期为1天
	private static final long TOKEN_TTL = 24 * 60 * 60 * 1000;

	/**
	 * 从配置文件获取token签名密钥
	 * 由于{@code @Value}无法直接注入静态成员，故采用{@code setter}方法参数注入
	 *
	 * @param secretString 配置文件中的密钥串
	 */
	@Value("${token.secretKey64}")
	private void setSecretString(String secretString) {
		Token.secretString = secretString;
	}

	/**
	 * 获取签名密钥
	 *
	 * @return 签名密钥
	 */
	private static SecretKey getSecretKey() {
		return Keys.hmacShaKeyFor(secretString.getBytes(StandardCharsets.UTF_8));
	}

	/**
	 * 获取签名密钥字符串
	 *
	 * @return 签名密钥字符串
	 */
	public static String getSecretString() {
		return secretString;
	}

	/**
	 * 生成token
	 *
	 * @param claimsMap payload信息，以{@code Map}方式
	 * @return token字符串
	 */
	public static String createToken(Map<String, Object> claimsMap) {
		SecretKey secretKey = getSecretKey();

		long currentTimeMillis = System.currentTimeMillis();
		Date currentTime = new Date(currentTimeMillis);

		long expirationTimeMillis = currentTimeMillis + TOKEN_TTL;
		Date expirationTime = new Date(expirationTimeMillis);

		return Jwts.builder()
				.setClaims(claimsMap)
				.setIssuedAt(currentTime)
				.setExpiration(expirationTime)
				.signWith(secretKey, SignatureAlgorithm.HS256)
				.compact();
	}

	/**
	 * 生成token(测试用)
	 *
	 * @param claimsMap payload信息，以{@code Map}方式
	 * @param ttlMillis token超时时间
	 * @return token字符串
	 */
	public static String createToken(Map<String, Object> claimsMap, long ttlMillis) {
		SecretKey secretKey = getSecretKey();

		long currentTimeMillis = System.currentTimeMillis();
		Date currentTime = new Date(currentTimeMillis);

		long expirationTimeMillis = currentTimeMillis + ttlMillis;
		Date expirationTime = new Date(expirationTimeMillis);

		return Jwts.builder()
				.setClaims(claimsMap)
				.setIssuedAt(currentTime)
				.setExpiration(expirationTime)
				.signWith(secretKey, SignatureAlgorithm.HS256)
				.compact();
	}

	/**
	 * 解析token的payload部分
	 *
	 * @param token token字符串
	 * @return payload信息
	 */
	public static Claims parseToken(String token) throws JwtException {
		SecretKey secretKey = getSecretKey();
		return Jwts.parserBuilder()
				.setSigningKey(secretKey)
				.build()
				.parseClaimsJws(token)
				.getBody();
	}

	/**
	 * 验证token
	 *
	 * @param token token字符串
	 * @return boolean 判断token是否合法
	 */
	public static Boolean verifyToken(String token) {
		AtomicBoolean isCaught = new AtomicBoolean(false);
		try {
			Token.parseToken(token);
		} catch (IllegalArgumentException | UnsupportedJwtException | MalformedJwtException | SignatureException | ExpiredJwtException e) {
			isCaught.set(true);
		}
		return !isCaught.get();
	}

}
